package com.xhd.demo.lock;

import java.util.concurrent.locks.StampedLock;

public class StampedLockDemo {
    /**
     * 定义坐标点
     */
    private double x;
    private double y;
    /**
     * 锁实例
     */
    private final StampedLock stampedLock = new StampedLock();

    /**
     * 移动坐标点
     * 使用排它锁，写锁
     * @param deltaX
     * @param deltaY
     */
    public void move(double deltaX, double deltaY) {
        long stamp = stampedLock.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            //释放锁
            stampedLock.unlockWrite(stamp);
        }
    }

    /**
     * 计算到原点的距离
     * 乐观读锁 -> 共享读锁(悲观获取)
     * @return
     */
    public double distanceFromOrgin() {
        //尝试获取乐观读锁
        long stamp = stampedLock.tryOptimisticRead();
        //将变量copy到方法栈中
        double currentX = x;
        double currentY = y;
        try {
            //检查乐观读锁票据后，锁有没有被抢占
            if (!stampedLock.validate(stamp)) {
                //悲观获取共享读锁，获取不到则挂起线程
                stamp = stampedLock.readLock();
                //变量复制到方法栈中
                currentX = x;
                currentY = y;
            }
        } finally {
            //释放共享读锁
            stampedLock.unlockRead(stamp);
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    /**
     * 如果坐标点在原点，则移动该坐标
     * 读锁 -> 升级为写锁 -> 升级失败则释放读锁，获取写锁
     */
    public void moveIfAtOrigin(double deltaX, double deltaY) {
        //获取读锁
        long stamp = stampedLock.tryOptimisticRead();
        try {
            //判断坐标是否在原点，在原点则移动坐标
            while (x == 0.0 && y == 0.0) {
                //尝试升级为写锁，升级成功，则更新坐标值，退出循环
                stamp = stampedLock.tryConvertToWriteLock(stamp);
                if (stamp != 0L) {
                    x += deltaX;
                    y += deltaY;
                    break;
                } else {
                    //释放读锁
                    stampedLock.unlockRead(stamp);
                    //获取独占写锁，继续循环
                    stamp = stampedLock.writeLock();
                }

            }
        } finally {
            //释放锁
            stampedLock.unlock(stamp);
        }
    }
}
